package com.infolab.aims.web;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperRunManager;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;

import com.infolab.aims.GlobalConstant;
import com.infolab.aims.common.AbstractController;
import com.infolab.aims.common.CommonUtil;
import com.infolab.aims.common.ParameterDateQueryHelper;
import com.infolab.aims.common.bean.SearchCriteria;
import com.infolab.aims.common.bean.SearchResult;
import com.infolab.aims.common.exception.DataStoreException;
import com.infolab.aims.domain.Department;
import com.infolab.aims.domain.Location;
import com.infolab.aims.domain.Vendor;
import com.infolab.aims.domain.asset.AssetEquipmentPK;
import com.infolab.aims.domain.asset.AssetModel;
import com.infolab.aims.domain.purchase.PurchaseOrder;
import com.infolab.aims.domain.purchase.PurchaseOrderDetail;
import com.infolab.aims.domain.purchase.PurchaseOrderRepair;
import com.infolab.aims.service.intf.AssetModelService;
import com.infolab.aims.service.intf.AssetTransactionFacadeService;
import com.infolab.aims.service.intf.PurchaseOrderService;

@Controller
@RequestMapping("/purchaseOrder")
@SessionAttributes(value = PurchaseOrderController.SESSION_SEARCH_CRITERIA)
public class PurchaseOrderController extends AbstractController {

	public static final String SESSION_SEARCH_CRITERIA = "searchCriteria";
	public static final String CONTEXT_PATH = "/purchaseOrder";
	public static final String JSP_MAIN_FORM = "purchaseOrderForm";
	public static final String JSP_MAIN_FORM_REPAIR = "purchaseOrderFormForRepairType";
	public static final String JSP_MAIN_SEARCH = "purchaseOrderSearch";

	@Autowired
	PurchaseOrderService purchaseOrderService;

	@Autowired
	AssetModelService assetModelService;

	@Autowired
	AssetTransactionFacadeService assetTransactionFacadeService;

	Log logger = LogFactory.getLog(getClass());

	@ModelAttribute(PurchaseOrderController.SESSION_SEARCH_CRITERIA)
	public SearchCriteria getPurchaseOrderSearchCriteria() {
		return new SearchCriteria();
	}

	@RequestMapping(value = "search")
	public String searchPurchaseOrder(@ModelAttribute(PurchaseOrderController.SESSION_SEARCH_CRITERIA) SearchCriteria searchCriteria,
			@ModelAttribute("currentSearchCriteria") SearchCriteria currentSearchCriteria, Model model, HttpServletRequest request) {

		Date dateFrom = null;
		Date dateTo = null;

		try {
			if (!checkNullEmpty(searchCriteria.getSearchCriteria("dateFrom"))) {
				dateFrom = new SimpleDateFormat(GlobalConstant.DATE_FORMAT_JAVA).parse(searchCriteria.getSearchCriteria("dateFrom").toString());
			}
		} catch (ParseException e) {
			searchCriteria.addSearchCriteria("dateFrom", "");
		}

		try {

			if (!checkNullEmpty(searchCriteria.getSearchCriteria("dateTo"))) {
				dateTo = new SimpleDateFormat(GlobalConstant.DATE_FORMAT_JAVA).parse(searchCriteria.getSearchCriteria("dateTo").toString());
			}
		} catch (ParseException e) {
			searchCriteria.addSearchCriteria("dateTo", "");
		}

		Department accessDepartment = getCurrentDepartment(request);
		if (accessDepartment != null)
			searchCriteria.addSearchCriteria("accessDepartmentId", accessDepartment.getDepartmentId());

		if (currentSearchCriteria.getCurPage() != null)
			searchCriteria.setCurPage(currentSearchCriteria.getCurPage());

		if (!checkNullEmpty(dateFrom) && !checkNullEmpty(dateTo)) {

			searchCriteria.addSearchCriteria("purchaseOrderDate", new ParameterDateQueryHelper("dateCreated", dateFrom, dateTo));
		}

		else if (!checkNullEmpty(dateFrom) && checkNullEmpty(dateTo)) {
			searchCriteria.addSearchCriteria("purchaseOrderDate", new ParameterDateQueryHelper("dateCreated", dateFrom, null));
		}

		else if (checkNullEmpty(dateFrom) && !checkNullEmpty(dateTo)) {
			searchCriteria.addSearchCriteria("purchaseOrderDate", new ParameterDateQueryHelper("dateCreated", null, dateTo));
		}

		else {
			searchCriteria.addSearchCriteria("purchaseOrderDate", new ParameterDateQueryHelper("dateCreated", null, null));
		}

		SearchResult searchResult = purchaseOrderService.searchPurchaseOrder(searchCriteria);
		model.addAttribute(searchResult);

		return JSP_MAIN_SEARCH;
	}

	// @RequestMapping(value={"create","create/{type}"}, method =
	// RequestMethod.GET)
	@RequestMapping(value = "create", method = RequestMethod.GET)
	public String createPurchaseOrder(Model model, HttpServletRequest request) {

		populatePurchaseOrder(model, null, request);
		PurchaseOrder purchaseOrder = new PurchaseOrder();
		purchaseOrder.setAccessDepartment(getCurrentDepartment(request));
		model.addAttribute(purchaseOrder);
		return JSP_MAIN_FORM;

	}

	// @RequestMapping(value={"create","create/{type}"}, method =
	// RequestMethod.GET)
	@RequestMapping(value = "creates", method = RequestMethod.GET)
	public String createPurchaseOrderForRepair(Model model, HttpServletRequest request) {

		populatePurchaseOrder(model, null, request);
		PurchaseOrder purchaseOrder = new PurchaseOrder();
		purchaseOrder.setAccessDepartment(getCurrentDepartment(request));
		model.addAttribute(purchaseOrder);
		return JSP_MAIN_FORM_REPAIR;

	}

	@RequestMapping(value = "creates", method = RequestMethod.POST)
	public String createPurchaseOrder(@ModelAttribute PurchaseOrder purchaseOrder, BindingResult result, Model model, HttpServletRequest request) {

		String poType = purchaseOrder.getPurchaseOrderType() != null ? (String) purchaseOrder.getPurchaseOrderType() : "";
		populatePurchaseOrder(model, null, request);
		purchaseOrder.setAccessDepartment(getCurrentDepartment(request));

		if (poType.equals("") || poType.equals(PurchaseOrder.PURCHASEORDERTYPE_NEW)) {
			// model.addAttribute(purchaseOrder);
			return JSP_MAIN_FORM;
		} else {
			purchaseOrder.setAssetType(AssetModel.ASSETTYPE_EQUIPMENT);
			// model.addAttribute(purchaseOrder);
			return JSP_MAIN_FORM_REPAIR;
		}

	}

	private void populatePurchaseOrder(Model model, PurchaseOrder purchaseOrder, HttpServletRequest request) {
		HashMap poTypeMap = new HashMap();
		poTypeMap.put(PurchaseOrder.PURCHASEORDERTYPE_NEW, "New Asset");
		poTypeMap.put(PurchaseOrder.PURCHASEORDERTYPE_REPAIR, "Repair");
		model.addAttribute("poTypeMap", poTypeMap);

		if (purchaseOrder != null && !(checkNullEmpty(purchaseOrder.getAssetType()))) {
			SearchCriteria searchCriteria = new SearchCriteria();
			searchCriteria.addSearchCriteria("assetType", (String) purchaseOrder.getAssetType());
			List<AssetModel> assetModels = assetModelService.findAssetModels(searchCriteria);
			model.addAttribute("assetModelList", assetModels);
		}

		List<Vendor> vendorList = (List<Vendor>) request.getSession().getServletContext().getAttribute(GlobalConstant.GLOBAL_VENDOR);
		List<Vendor> newVendorList = new ArrayList<Vendor>();
		List<Vendor> repairVendorList = new ArrayList<Vendor>();

		if (vendorList != null) {
			for (Vendor v : vendorList) {
				if (StringUtils.equalsIgnoreCase(v.getVendorType(), "NEW"))
					newVendorList.add(v);
				else
					repairVendorList.add(v);
			}
		}

		model.addAttribute("vendorList", vendorList);
		model.addAttribute("newVendorList", newVendorList);
		model.addAttribute("repairVendorList", repairVendorList);

	}

	@InitBinder
	public void initBinder(WebDataBinder binder) {
		SimpleDateFormat dateFormat = new SimpleDateFormat(GlobalConstant.DATE_FORMAT_JAVA);
		dateFormat.setLenient(false);
		binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
	}

	@RequestMapping(value = "create", method = RequestMethod.POST)
	public String savePurchaseOrder(@ModelAttribute PurchaseOrder purchaseOrder, BindingResult result, Model model, HttpServletRequest request) {

		String target = null;

		if (purchaseOrder.isRepairPurchaseOrder()) {
			purchaseOrder.setAssetType(AssetModel.ASSETTYPE_EQUIPMENT);
			target = JSP_MAIN_FORM_REPAIR;
		} else {
			target = JSP_MAIN_FORM;
		}
		populatePurchaseOrder(model, purchaseOrder, request);
		// model.addAttribute(purchaseOrder);

		if (validatePurchaseOrder(purchaseOrder, result)) {
			return target;
		}
		purchaseOrder.setCreatedBy(getCurrentUserName(request));

		try {
			Department accessDepartment = getCurrentDepartment(request);
			purchaseOrder.setAccessDepartment(accessDepartment);

			prepareAuditObject(purchaseOrder, request);

			assetTransactionFacadeService.createPurchaseOrder(purchaseOrder);
		} catch (DataStoreException e) {
			if (e.isUniqueConstraint()) {
				if (StringUtils.equalsIgnoreCase("purchaseOrderCode", e.getColumnName()))
					result.rejectValue("purchaseOrderCode", "error.exist", new String[] { purchaseOrder.getPurchaseOrderCode() }, "");
				else
					result.reject("error.unknown", new String[] { e.getMessage() }, "");
			} else
				result.reject("error.unknown", new String[] { e.getMessage() }, "");

			return target;
		}
		setSuccessMessage(getMessageResources("success.create", "purchaseOrder " + purchaseOrder.getPurchaseOrderCode()));

		return "redirect:" + CONTEXT_PATH + "/update/" + purchaseOrder.getPurchaseOrderId();
	}

	@RequestMapping(value = "update/{id}", method = RequestMethod.POST)
	public String updatePurchaseOrder(@PathVariable Integer id, @ModelAttribute PurchaseOrder purchaseOrder, BindingResult result, Model model,
			HttpServletRequest request) {

		/*
		 * List<AssetModel> assetModels=
		 * assetModelService.findAssetModels(null);
		 * model.addAttribute("assetModelList",assetModels);
		 */

		String target = null;

		if (purchaseOrder.isRepairPurchaseOrder()) {
			purchaseOrder.setAssetType(AssetModel.ASSETTYPE_EQUIPMENT);
			target = JSP_MAIN_FORM_REPAIR;
		} else {
			target = JSP_MAIN_FORM;
		}
		populatePurchaseOrder(model, purchaseOrder, request);
		if (validatePurchaseOrder(purchaseOrder, result)) {
			return target;
		}

		purchaseOrder.setModifiedBy(getCurrentUserName(request));

		try {

			/*
			 * PurchaseOrder originalPurchaseOrder =
			 * purchaseOrderService.getPurchaseOrder(id);
			 * if(originalPurchaseOrder!=null) {
			 * if(originalPurchaseOrder.getPurchaseOrderStatus
			 * ().equalsIgnoreCase(PurchaseOrder.PURCHASEORDERSTATUS_NEW))
			 */
			prepareAuditObject(purchaseOrder, request);
			purchaseOrderService.updatePurchaseOrder(purchaseOrder);
			/*
			 * else result.rejectValue("purchaseOrderCode",
			 * "error.inconsistent", new
			 * String[]{purchaseOrder.getPurchaseOrderCode()}, ""); }
			 * 
			 * else result.rejectValue("purchaseOrderCode", "error.deleted", new
			 * String[]{purchaseOrder.getPurchaseOrderCode()}, "");
			 */
		} catch (DataStoreException e) {
			if (e.isUniqueConstraint()) {
				if (StringUtils.equalsIgnoreCase("purchaseOrderCode", e.getColumnName()))
					result.rejectValue("purchaseOrderCode", "error.exist", new String[] { purchaseOrder.getPurchaseOrderCode() }, "");
				else
					result.reject("error.unknown", new String[] { e.getMessage() }, "");
			} else
				result.reject("error.unknown", new String[] { e.getMessage() }, "");

			return target;
		}

		setSuccessMessage(getMessageResources("success.update", "purchaseOrder " + purchaseOrder.getPurchaseOrderCode()));

		return "redirect:" + CONTEXT_PATH + "/update/" + purchaseOrder.getPurchaseOrderId();

	}

	@RequestMapping(value = { "update/{id}", "detail/{id}" }, method = RequestMethod.GET)
	public String detailPurchaseOrder(@PathVariable Integer id, Model model, HttpServletRequest request) {

		PurchaseOrder purchaseOrder = purchaseOrderService.getPurchaseOrder(id);
		populatePurchaseOrder(model, purchaseOrder, request);
		model.addAttribute(purchaseOrder);

		if (purchaseOrder.isRepairPurchaseOrder()) {
			return JSP_MAIN_FORM_REPAIR;
		} else {
			return JSP_MAIN_FORM;
		}

	}

	@RequestMapping(value = { "delete/{id}" }, method = RequestMethod.GET)
	public String deletePurchaseOrder(@PathVariable Integer id, Model model, HttpServletRequest request) {

		PurchaseOrder purchaseOrder = purchaseOrderService.getPurchaseOrder(id);

		purchaseOrderService.deletePurchaseOrder(purchaseOrder);

		return "redirect:" + CONTEXT_PATH + "/search";
	}

	@RequestMapping(value = { "close/{id}" }, method = RequestMethod.GET)
	public String closePurchaseOrder(@PathVariable Integer id, Model model, HttpServletRequest request) {

		PurchaseOrder purchaseOrder = purchaseOrderService.getPurchaseOrder(id);

		purchaseOrderService.closePurchaseOrder(purchaseOrder);

		setSuccessMessage(getMessageResources("success.close", "purchaseOrder " + purchaseOrder.getPurchaseOrderCode()));

		return "redirect:" + CONTEXT_PATH + "/update/" + purchaseOrder.getPurchaseOrderId();
	}

	@RequestMapping(value = { "print/{id}" }, method = RequestMethod.GET)
	public void printPurchaseOrder(@PathVariable Integer id, Model model, HttpServletRequest request, HttpServletResponse response)
			throws IOException {

		try {
			ServletOutputStream servletOutputStream = response.getOutputStream();
			InputStream reportStream = null;
			PurchaseOrder purchaseOrder = purchaseOrderService.getPurchaseOrder(id);

			Vendor vendor = purchaseOrder.getVendor();
			Department department = purchaseOrder.getAccessDepartment();
			HashMap details = new HashMap();
			details.put("purchaseOrderCode", purchaseOrder.getPurchaseOrderCode());
			details.put("vendorName", vendor.getVendorName());
			details.put("assetType", purchaseOrder.getAssetType());
			details.put("receivingLocation", purchaseOrder.getLocation().getLocationName());
			details.put("companyCode", purchaseOrder.getCompanyCode());
			details.put("poDate", purchaseOrder.getPurchaseOrderDateToDisplay());
			details.put("poType", purchaseOrder.getPurchaseOrderType());
			details.put("accessDepartment", department != null ? department.getDepartmentName() : "-");
			details.put("poStatus", purchaseOrder.getPurchaseOrderStatus());

			if (purchaseOrder.isNewPurchaseOrder()) {

				List<PurchaseOrderDetail> list = purchaseOrder.getPurchaseOrderDetails();

				int totalItems = list != null ? list.size() : 0;
				details.put("totalItems", totalItems + "");

				reportStream = getClass().getClassLoader().getResourceAsStream("report/PurchaseOrderNew.jasper");
				JasperRunManager.runReportToPdfStream(reportStream, servletOutputStream, details, new JRBeanCollectionDataSource(list));
			}

			else if (purchaseOrder.isRepairPurchaseOrder()) {
				List<PurchaseOrderRepair> list = purchaseOrder.getPurchaseOrderRepairs();
				int totalItems = list != null ? list.size() : 0;
				details.put("totalItems", totalItems + "");
				reportStream = getClass().getClassLoader().getResourceAsStream("report/PurchaseOrderRepair.jasper");
				JasperRunManager.runReportToPdfStream(reportStream, servletOutputStream, details, new JRBeanCollectionDataSource(list));
			}

			response.setContentType("application/pdf");
			servletOutputStream.flush();
			servletOutputStream.close();
		} catch (JRException e) {
			// display stack trace in the browser
			logger.error(CommonUtil.getStackTrace(e));
			StringWriter stringWriter = new StringWriter();
			PrintWriter printWriter = new PrintWriter(stringWriter);
			e.printStackTrace(printWriter);
			response.setContentType("text/plain");
			response.getOutputStream().print(stringWriter.toString());
		}

	}

	private boolean validatePurchaseOrder(PurchaseOrder purchaseOrder, BindingResult result) {

		if (purchaseOrder.getAccessDepartment().getDepartmentId() == null)
			purchaseOrder.setAccessDepartment(null);

		if (purchaseOrder.getVendor().getVendorId() == null) {
			purchaseOrder.setVendor(null);
		}

		if (purchaseOrder.getLocation().getLocationId() == null) {
			purchaseOrder.setLocation(null);
		}

		validator.validate(purchaseOrder, result);

		if (!result.hasErrors()) {

			/*
			 * if(purchaseOrder.isRepairPurchaseOrder()){
			 * Vector<AssetEquipmentPK> vector = new Vector<AssetEquipmentPK>();
			 * List<PurchaseOrderRepair> purchaseOrderRepairs =
			 * purchaseOrder.getPurchaseOrderRepairs();
			 * if(purchaseOrderRepairs!=null) { for (int i = 0; i <
			 * purchaseOrderRepairs.size(); i++) { PurchaseOrderRepair
			 * purchaseOrderRepair = purchaseOrderRepairs.get(i);
			 * AssetEquipmentPK assetEquipmentPK= new
			 * AssetEquipmentPK(purchaseOrderRepair.getAssetModel(),
			 * purchaseOrderRepair.getSerialNo());
			 * if(vector.contains(assetEquipmentPK)) {
			 * result.reject("error.order.duplicateRecord"); break; } else {
			 * vector.addElement(assetEquipmentPK); } } } }
			 */

			if (purchaseOrder.isRepairPurchaseOrder()) {
				Vector<AssetEquipmentPK> vector = new Vector<AssetEquipmentPK>();
				List<PurchaseOrderRepair> purchaseOrderRepairs = purchaseOrder.getPurchaseOrderRepairs();
				if (purchaseOrderRepairs != null) {
					for (int i = 0; i < purchaseOrderRepairs.size(); i++) {
						try {

							PurchaseOrderRepair purchaseOrderRepair = purchaseOrderRepairs.get(i);
							if (!purchaseOrderRepair.isDeleted()) {
								SearchCriteria searchCriteria = new SearchCriteria();
								searchCriteria.addSearchCriteria("assetModelId", purchaseOrderRepair.getAssetModel().getAssetModelId());
								AssetEquipmentPK assetEquipmentPK = new AssetEquipmentPK(assetModelService.findAssetModel(searchCriteria),
										purchaseOrderRepair.getSerialNo());
								if (vector.contains(assetEquipmentPK)) {
									result.reject("error.order.duplicateRecord");
									break;
								} else {
									vector.addElement(assetEquipmentPK);
								}
							}
						} catch (Exception e) {
							logger.error(CommonUtil.getStackTrace(e));
						}
					}
				}
			}
		}

		return result.hasErrors();
	}

	private void prepareAuditObject(PurchaseOrder purchaseOrder, HttpServletRequest request) {
		/* audit start */
		Location loc = findLocation(request, purchaseOrder.getLocation().getLocationId());
		purchaseOrder.setLocation(loc);
		Vendor ven = findVendor(request, purchaseOrder.getVendor().getVendorId());
		purchaseOrder.setVendor(ven);
		if (purchaseOrder.isNewPurchaseOrder()) {
			List<PurchaseOrderDetail> list = purchaseOrder.getPurchaseOrderDetails();
			if (CollectionUtils.isNotEmpty(list)) {

				for (PurchaseOrderDetail detail : list) {
					if (detail.getAssetModel() != null) {
						Integer assetModelId = detail.getAssetModel().getAssetModelId();
						AssetModel tmp = findAssetModel(request, assetModelId);
						detail.setAssetModel(tmp);
					}
				}
			}
		}

		else if (purchaseOrder.isRepairPurchaseOrder()) {
			List<PurchaseOrderRepair> list = purchaseOrder.getPurchaseOrderRepairs();
			if (CollectionUtils.isNotEmpty(list)) {

				for (PurchaseOrderRepair detail : list) {
					if (detail.getAssetModel() != null) {
						Integer assetModelId = detail.getAssetModel().getAssetModelId();
						AssetModel tmp = findAssetModel(request, assetModelId);
						detail.setAssetModel(tmp);
					}
				}
			}
		}
		/* audit end */
	}

	public boolean checkNullEmpty(Object obj) {

		boolean flag = true;
		if (obj != null && !obj.equals("")) {
			flag = false;
		}
		return flag;

	}

}
